/*
 * @Description:
 * @Autor: lcz
 * @Date: 2022-07-14 15:59:57
 * @LastEditors: lcz
 * @LastEditTime: 2022-07-15 10:56:51
 */
import classNames from 'classnames'
import {
  createElement,
  forwardRef,
  ForwardRefExoticComponent,
  PropsWithoutRef,
  RefAttributes,
  Children,
  isValidElement,
} from 'react'
import warn from 'rc-util/lib/warning'
import { useInsertStyles } from '../../hooks/useInsertStyles'

export const BuildinIconScriptUrl = 'widgets/resources/font_8d5l8fzk5b87iudi.js'
const customCache = new Set<string>()
customCache.add(BuildinIconScriptUrl)

export interface CustomIconComponentProps {
  width: string | number
  height: string | number
  fill: string
  viewBox?: string
  className?: string
  style?: Omit<React.CSSProperties, 'fontSizeAdjust'>
  // style?: any
}
export const svgBaseProps = {
  width: '1em',
  height: '1em',
  fill: 'currentColor',
  'aria-hidden': 'true',
  focusable: 'false',
}
export interface IconBaseProps extends React.HTMLProps<HTMLDivElement> {
  spin?: boolean
  rotate?: number
}

export interface IconFontProps extends IconBaseProps {
  type: string
}

export interface CustomIconOptions {
  extraCommonProps?: { [key: string]: any }
}

export interface IconComponentProps extends IconBaseProps {
  viewBox?: string
  ariaLabel?: React.AriaAttributes['aria-label']
  style?: Omit<React.CSSProperties, 'fontSizeAdjust'>
  // style?: any
}

export function warning(valid: boolean, message: string) {
  warn(valid, `[@ant-design/icons] ${message}`)
}

const Icon = forwardRef<HTMLDivElement, IconComponentProps>((props, ref) => {
  const {
    // affect outter <i>...</i>
    className,
    style,

    viewBox,
    spin,
    rotate,

    tabIndex,

    // children
    children,
  } = props

  warning(Boolean(children), 'Should have `children` prop.')

  useInsertStyles()

  const classString = classNames('anticon', className)

  const svgClassString = classNames({
    'anticon-spin': !!spin,
  })

  const svgStyle: Omit<React.CSSProperties, 'fontSizeAdjust'> = rotate
    ? {
        msTransform: `rotate(${rotate}deg)`,
        transform: `rotate(${rotate}deg)`,
      }
    : {}
  svgStyle.height = style?.height
  svgStyle.width = style?.width

  const innerSvgProps: CustomIconComponentProps = {
    ...svgBaseProps,
    className: svgClassString,
    style: svgStyle,
    viewBox,
  }

  if (!viewBox) {
    delete innerSvgProps.viewBox
  }

  let iconTabIndex = tabIndex
  if (iconTabIndex === undefined) {
    iconTabIndex = -1
  }

  warning(
    Boolean(viewBox) ||
      (Children.count(children) === 1 && isValidElement(children) && Children.only(children).type === 'use'),
    'Make sure that you provide correct `viewBox`' + ' prop (default `0 0 1024 1024`) to the icon.'
  )

  return (
    <div style={style} tabIndex={iconTabIndex} ref={ref} className={classString}>
      <svg className={svgClassString} {...innerSvgProps} viewBox={viewBox}>
        {children}
      </svg>
    </div>
  )
})

Icon.displayName = 'AntdIcon'

// 创建iconFont
export default function create(
  options: CustomIconOptions = {}
): ForwardRefExoticComponent<PropsWithoutRef<IconFontProps> & RefAttributes<HTMLDivElement>> {
  const { extraCommonProps = {} } = options

  const Iconfont = forwardRef<HTMLDivElement, IconFontProps>((props, ref) => {
    const { type, children, ...restProps } = props

    // children > type
    let content: React.ReactNode = null
    if (props.type) {
      content = <use xlinkHref={`#${type}`} />
    }
    if (children) {
      content = children
    }
    return (
      <Icon
        {...extraCommonProps}
        {...restProps}
        ref={ref}
        className={props.className}
        style={props.style}
        name={props.name}
        tabIndex={props.tabIndex}
      >
        {content}
      </Icon>
    )
  })

  Iconfont.displayName = 'Iconfont'

  return Iconfont
}

//加载字体
export function loadIconLib(scriptUrl: string | string[] | undefined) {
  /**
   * DOM API required.
   * Make sure in browser environment.
   * The Custom Icon will create a <script/>
   * that loads SVG symbols and insert the SVG Element into the document body.
   */
  if (
    scriptUrl &&
    typeof document !== 'undefined' &&
    typeof window !== 'undefined' &&
    typeof document.createElement === 'function'
  ) {
    if (Array.isArray(scriptUrl)) {
      // 因为iconfont资源会把svg插入before，所以前加载相同type会覆盖后加载，为了数组覆盖顺序，倒叙插入
      if (scriptUrl.length > 0) {
        createScriptUrlElements(scriptUrl.reverse())
      }
    } else {
      createScriptUrlElements([scriptUrl])
    }
  }
}

//判断存不存在,并且判断是否有重复加载
function isValidCustomScriptUrl(scriptUrl: string): boolean {
  return Boolean(typeof scriptUrl === 'string' && scriptUrl.length && !customCache.has(scriptUrl))
}

//递归 原生加载图标
function createScriptUrlElements(scriptUrls: string[], index = 0): void {
  const currentScriptUrl = scriptUrls[index]
  if (isValidCustomScriptUrl(currentScriptUrl)) {
    const script = document.createElement('script')
    script.setAttribute('src', currentScriptUrl)
    script.setAttribute('data-namespace', currentScriptUrl)
    if (scriptUrls.length > index + 1) {
      script.onload = () => {
        createScriptUrlElements(scriptUrls, index + 1)
      }
      script.onerror = () => {
        createScriptUrlElements(scriptUrls, index + 1)
      }
    }
    customCache.add(currentScriptUrl)
    document.body.appendChild(script)
  }
}
